// Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Authors: Zhangyi Chen(chenzhangyi01@baidu.com)

#ifndef  BRAFT_CONFIGURATION_MANAGER_H
#define  BRAFT_CONFIGURATION_MANAGER_H

#include "braft/configuration.h"         // Configuration
#include "braft/log_entry.h"             // LogId

namespace braft {

struct ConfigurationEntry {
    LogId id;
    Configuration conf;
    Configuration old_conf;

    ConfigurationEntry() {}
    ConfigurationEntry(const LogEntry& entry) {
        id = entry.id;
        conf = *(entry.peers);
        if (entry.old_peers) {
            old_conf = *(entry.old_peers);
        }
    }

    bool stable() const { return old_conf.empty(); }
    bool empty() const { return conf.empty(); }
    void list_peers(std::set<PeerId>* peers) {
        peers->clear();
        conf.append_peers(peers);
        old_conf.append_peers(peers);
    }
    bool contains(const PeerId& peer) const
    { return conf.contains(peer) || old_conf.contains(peer); }
};

// Manager the history of configuration changing
/*
 在Braft中，复制组Configuration的变更通过Raft Log完成，ConfigurationManager建立LogId到Configuration的映射来跟踪记录这些变更。
 也可以认为ConfigurationManager把Raft Log中的所有Configuration Log Entry全部单独抽出来管理，当LogManager truncate时它也跟着truncate；
 当做Snapshot时，也要把Snapshot的last logid对应的Configuration提取出来写到SnapshotMeta中。
*/
class ConfigurationManager {
public:
    ConfigurationManager() {}
    ~ConfigurationManager() {}

    // add new configuration at index
    int add(const ConfigurationEntry& entry);

    // [1, first_index_kept) are being discarded
    void truncate_prefix(int64_t first_index_kept);

    // (last_index_kept, infinity) are being discarded
    void truncate_suffix(int64_t last_index_kept);

    void set_snapshot(const ConfigurationEntry& snapshot);

    void get(int64_t last_included_index, ConfigurationEntry* entry);

    const ConfigurationEntry& last_configuration() const;

private:

    std::deque<ConfigurationEntry> _configurations;
    /*
     为了防止重启时config log的丢失（已做snapshot后被truncate掉），
     所以在做snapshot时需要把当时的Configuration加入snapshot的meta中，
     该 _snapshot 便是对应记录在 snapshot meta 中的 Configuration
    */
    ConfigurationEntry _snapshot;
    /*
     把conf加入snapshot是raft协议的要求，而且也能保证node重启之后的状态恢复。
     但是这要付出add_peer时，新加入的node会先apply一个错误的conf，然后从leader
     分发的raft log中再把最新的conf apply上去，在这两个时间之间，follower的conf是错误的。
    */
};

}  //  namespace braft

#endif  //BRAFT_CONFIGURATION_MANAGER_H
